import { Injectable } from '@angular/core';
import { Action, NgxsOnInit, Selector, State, StateContext } from '@ngxs/store';
import { concatMap, mergeMap, tap } from 'rxjs/operators';
import { Sentence } from 'src/app/model/sentence';
import { AuthService, SentenceService } from 'src/app/service';
import { UserInfoService } from 'src/app/service/user.info.service';
import { AddSentence, DeleteSentence, GetSentences, GetUserInfo, UpdateSentence } from '../actions/app.action';
import { Login, Logout, SwitchAutoPlaying, UpdateChapterNo, UpdateSequenceNo, UpdateUserInfo, UpdateWaitTime } from './../actions/app.action';

export class AppStateModel {
  sentences: Sentence[];
  currentSentence: Sentence;
  currentChapterNo: number;
  currentSequenceNo: number;
  totalQuantity: number;
  autoPlaying: boolean;
  waitTime: number;
  userName: string;
  isLogin: boolean;
}

@State<AppStateModel>({
  name: 'app',
  defaults: {
    sentences: [],
    currentSentence: null,
    currentChapterNo: 1,
    currentSequenceNo: 1,
    totalQuantity: 0,
    autoPlaying: false,
    waitTime: 5,
    userName: '',
    isLogin: false,
  },
})
@Injectable()
export class AppState implements NgxsOnInit {
  constructor(private sentenceService: SentenceService, private userInfoService: UserInfoService, private authService: AuthService) {}

  @Selector()
  public static getSentences(state: AppStateModel) {
    return state.sentences;
  }
  @Selector()
  public static getCurrentSentence(state: AppStateModel) {
    return state.currentSentence;
  }
  @Selector()
  public static getCurrentChapterNo(state: AppStateModel) {
    return state.currentChapterNo;
  }
  @Selector()
  public static getCurrentSequenceNo(state: AppStateModel) {
    return state.currentSequenceNo;
  }
  @Selector()
  public static getTotalQuantity(state: AppStateModel) {
    return state.totalQuantity;
  }
  @Selector()
  public static getAutoPlaying(state: AppStateModel) {
    return state.autoPlaying;
  }
  @Selector()
  public static getWaitTime(state: AppStateModel) {
    return state.waitTime;
  }
  @Selector()
  public static getIsLogin(state: AppStateModel) {
    return state.isLogin;
  }
  @Selector()
  public static getUserName(state: AppStateModel) {
    return state.userName;
  }

  @Action(AddSentence)
  public addSentence({ getState, setState }: StateContext<AppStateModel>, { payload }: AddSentence) {
    const state = getState();
    return this.sentenceService
      .collectNewSentence(payload)
      .pipe(mergeMap(() => this.sentenceService.getSentencesByChapterNo(state.currentChapterNo)))
      .pipe(
        tap((s) => {
          setState({
            ...state,
            sentences: s,
          });
        })
      );
  }

  @Action(GetSentences)
  public getSentence({ getState, patchState }: StateContext<AppStateModel>, { payload }: GetSentences) {
    const state = getState();
    console.log('AppState - getSentence - state:', state);
    console.log('AppState - getSentence - payload:', payload);
    console.log('AppState - getSentence - state.currentChapterNo :', state.currentChapterNo);
    const chapterNo = state.currentChapterNo + payload < 1 ? 1 : state.currentChapterNo + payload;
    console.log('AppState - getSentence - chapterNo:', chapterNo);

    return this.sentenceService.getSentencesByChapterNo(chapterNo).pipe(
      tap((ss) => {
        patchState({
          currentChapterNo: chapterNo,
          currentSequenceNo: state.currentSequenceNo,
          sentences: ss,
          /*state.currentSequenceNo start from 1,so need to minus 1*/
          currentSentence: ss.length === 0 ? null : ss[state.currentSequenceNo],
        });
      })
    );
  }
  @Action(UpdateSequenceNo)
  public updateSequenceNo({ getState, patchState }: StateContext<AppStateModel>, { payload }: UpdateSequenceNo) {
    const state = getState();
    const length = state.sentences.length;
    const sequenceNo = length === 0 ? 1 : payload;
    const sentence = length === 0 ? null : state.sentences[sequenceNo - 1];
    patchState({
      currentSequenceNo: sequenceNo,
      currentSentence: sentence,
    });
  }

  @Action(UpdateSentence)
  public updateSentence({ getState, patchState }: StateContext<AppStateModel>, { payload }: UpdateSentence) {
    const state = getState();
    return this.sentenceService.modify(payload).pipe(
      mergeMap(() => this.sentenceService.getSentencesByChapterNo(state.currentChapterNo)),
      tap((ss) => {
        patchState({
          sentences: ss,
          currentSentence: ss.length === 0 ? null : ss[state.currentSequenceNo - 1],
        });
      })
    );
  }

  @Action(DeleteSentence)
  public deleteSentence({ getState, patchState }: StateContext<AppStateModel>, { payload }: DeleteSentence) {
    const state = getState();
    return this.sentenceService.remove(payload).pipe(
      mergeMap(() => this.sentenceService.getSentencesByChapterNo(state.currentChapterNo)),
      tap((ss) => {
        patchState({
          sentences: ss,
          currentSentence: ss.length === 0 ? null : ss[state.currentSequenceNo - 1],
        });
      })
    );
  }

  @Action(GetUserInfo)
  public getUserInfo({ getState, patchState }: StateContext<AppStateModel>, {}: GetUserInfo) {
    const state = getState();

    return this.userInfoService.getUserInfo().pipe(
      tap((u) => {
        patchState({
          currentChapterNo: u.currentChapterNo,
          currentSequenceNo: u.currentSequenceNo,
          totalQuantity: u.totalQuantity,
          autoPlaying: u.autoPlaying,
          waitTime: u.waitTime,
        });
      })
    );
  }
  @Action(UpdateUserInfo)
  public updateUserInfo({ getState }: StateContext<AppStateModel>, {}: UpdateUserInfo) {
    const state = getState();
    return this.userInfoService.updateUserInfo(state.currentChapterNo, state.currentSequenceNo, state.waitTime, state.autoPlaying);
  }
  @Action(SwitchAutoPlaying)
  public switchAutoPlaying({ patchState }: StateContext<AppStateModel>, { payload }: SwitchAutoPlaying) {
    patchState({
      autoPlaying: payload,
    });
  }
  @Action(UpdateWaitTime)
  public updateWaitTime({ patchState }: StateContext<AppStateModel>, { payload }: UpdateWaitTime) {
    patchState({
      waitTime: payload,
    });
  }
  @Action(UpdateChapterNo)
  public updateChapterNo(ctx: StateContext<AppStateModel>, { payload }: UpdateChapterNo) {
    ctx.patchState({
      currentChapterNo: payload,
    });
    return ctx.dispatch(new GetSentences());
  }

  @Action(Login)
  public login({ patchState }: StateContext<AppStateModel>, { payload }: Login) {
    return this.authService.login({ email: payload.email, password: payload.password }).pipe(
      concatMap(() => this.userInfoService.getUserInfo()),
      tap((u) => {
        patchState({
          currentChapterNo: u.currentChapterNo,
          currentSequenceNo: u.currentSequenceNo,
          totalQuantity: u.totalQuantity,
          autoPlaying: u.autoPlaying,
          waitTime: u.waitTime,
          userName: payload.email,
          isLogin: true,
        });
      })
    );
  }

  @Action(Logout)
  public logout({ patchState }: StateContext<AppStateModel>, {}: Logout) {
    return this.authService.logout().pipe(
      tap(() => {
        patchState({
          userName: '',
          isLogin: false,
        });
      })
    );
  }
  public ngxsOnInit(ctx: StateContext<AppStateModel>): void {
    setInterval(() => {
      ctx.dispatch(UpdateUserInfo).subscribe();
    }, 600000);
    const ss = ctx.getState().sentences;
    if (!ss || ss.length === 0) {
      ctx.dispatch(new GetSentences()).subscribe(async (s) => {
        console.log(' -------------------------------------------');
        console.log('ngxsOnInit - ctx.dispatch - GetSentences - s:', s);
        console.log(' -------------------------------------------');
      });
    }
  }
}

